#include "SDCard.h"
#include "string.h"
#include "stdlib.h"
const char *SDCard::TypeToString[] =
{
	"V1",
	"V2",
	"V2HC"
};
const char *SDCard::ResaultToString[] =
{
	"ERROR",
	"TIMEOUT",
	"OK"
};
extern USART cout;
SDCard::SDResault_t SDCard::CheckReturn(uint8_t CMD, uint32_t Flag, uint32_t& Resp) const
{
//	cout << "Check:" << (uint32_t)CMD << endl;
	Flag |= SDIO_FLAG_CTIMEOUT;//默认检查超时
	while(not Port.GetFlagStatus(Flag));
	Port.ClearFlag(Flag);
	if(Port.GetFlagStatus(SDIO_FLAG_CTIMEOUT))
	{
		Port.ClearFlag(SDIO_FLAG_CTIMEOUT);
		return TIMEOUT;
	}
//	cout << "CMD Resp:" << (uint32_t)Port.GetCommandResponse() << endl;
	if(Port.GetCommandResponse() != CMD) return ERROR; //卡未响应CMD
//	cout.Printf("Resp:%#X\r\n", Port.GetResponse(SDIO_RESP1));
//	cout.Printf("Resp:%#X\r\n", Port.GetResponse(SDIO_RESP2));
//	cout.Printf("Resp:%#X\r\n", Port.GetResponse(SDIO_RESP3));
//	cout.Printf("Resp:%#X\r\n", Port.GetResponse(SDIO_RESP4));
	Resp = Port.GetResponse(SDIO_RESP1);
	return OK;
}
SDCard::SDResault_t SDCard::PowerOn()
{
	Port.SetPowerState(SDIO_PowerState_ON);//上电
	SDResault_t Res = ERROR;
	this->Type = V1;
	SDIO::CmdInitTypeDef Com;
	uint32_t Resp;
	//CMD0
	Com.CmdIndex = 0x00;
	Com.Argument = 0x00;
	Com.Response = SDIO_Response_No;
	Com.Wait = SDIO_Wait_No;
	Com.CPSM = SDIO_CPSM_Enable;
	Port.ClearFlag(SDIO_FLAG_CMDSENT);
	Port.ClearFlag(SDIO_FLAG_CMDREND);

	
	for(int i = 0; i < 74; i++)
	{
		Port.SendCommand(Com);
		while((not Port.GetFlagStatus(SDIO_FLAG_CMDSENT)) and (not Port.GetFlagStatus(SDIO_FLAG_CTIMEOUT)));
		Port.ClearFlag(SDIO_FLAG_CMDSENT);
		if(not Port.GetFlagStatus(SDIO_FLAG_CTIMEOUT))
			break;
	}

	//CMD8
	Com.CmdIndex = 0x08;
	Com.Argument = 0x1AA;
	Com.Response = SDIO_Response_Short;
	Port.SendCommand(Com);
	Res = this->CheckReturn(8, SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	this->Type = V2;

	bool Volt = false;
	do
	{
		//CMD55
		Com.CmdIndex = 55;
		Com.Argument = 0x00;
		Port.SendCommand(Com);
		Res = this->CheckReturn(55, SDIO_FLAG_CMDREND, Resp);
		if(Res != OK)
			return Res;

		//ACMD41
		Com.CmdIndex = 41;
		Com.Argument = 0xC0100000;
		Port.SendCommand(Com);
		this->CheckReturn(63, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
		Volt = ((Resp >> 31) == 1);
	}
	while(not Volt);
	this->OCR = Resp;
	if((this->OCR & 0x40000000) and (Type == V2))
		this->Type = V2HC;
	return Res;
}
SDCard::SDResault_t SDCard::Init()
{
	SDResault_t Res = ERROR;
	uint32_t Resp;
	SDIO::CmdInitTypeDef Com;
	Res = this->PowerOn();
	if(Port.GetPowerState() == 0) return ERROR;

	//CMD2
	Com.CmdIndex = 2;
	Com.Argument = 0;
	Com.Response = SDIO_Response_Long;
	Com.CPSM = SDIO_CPSM_Enable;
	Com.Wait = SDIO_Wait_No;
	Port.SendCommand(Com);
	Res = this->CheckReturn(63, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, this->CID[0]);
	if(Res != OK)
		return Res;
	this->CID[1] = Port.GetPeriphPointer()->RESP2;//保存CID信息
	this->CID[2] = Port.GetPeriphPointer()->RESP3;
	this->CID[3] = Port.GetPeriphPointer()->RESP4;

	//CMD3
	Com.CmdIndex = 3;
	Com.Argument = 0x00;
	Com.Response = SDIO_Response_Short;
	Port.SendCommand(Com);
	Res = this->CheckReturn(3, SDIO_FLAG_CMDREND | SDIO_FLAG_CCRCFAIL, Resp);
	if(Res != OK)
		return Res;
	if((Resp & 0xE000) == 0) //无错误
		this->RCA = Resp >> 16; //RCA
	else
		return ERROR;

	//CMD9
	Com.CmdIndex = 9;
	Com.Argument = this->RCA << 16;
	Com.Response = SDIO_Response_Long;
	Port.SendCommand(Com);
	Res = this->CheckReturn(63, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, this->CSD[0]);
	if(Res != OK)
		return Res;
	this->CSD[1] = Port.GetPeriphPointer()->RESP2;//保存CSD信息
	this->CSD[2] = Port.GetPeriphPointer()->RESP3;
	this->CSD[3] = Port.GetPeriphPointer()->RESP4;
	
	Com.CmdIndex = 7;
	Com.Argument = this->RCA << 16;
	Com.Response = SDIO_Response_Short;
	Port.SendCommand(Com);
	Res = this->CheckReturn(7, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	
	//ACMD6
	Com.CmdIndex = 55;
	Com.Argument = this->RCA<<16;
	Port.SendCommand(Com);
	Res = this->CheckReturn(55, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	
	Com.CmdIndex = 6;
	Com.Argument = 2;//4Bit
	Port.SendCommand(Com);
	Res = this->CheckReturn(6, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	
	SetBolckSize();//default 512Byte
	
	while(Port.GetFlagStatus(SDIO_FLAG_TXACT));
	
	SDIO(6,SDIO_BusWide_4b,SDIO_ClockEdge_Rising,SDIO_ClockBypass_Disable,SDIO_ClockPowerSave_Enable,SDIO_HardwareFlowControl_Enable);
	
	return Res;
}
SDCard::SDResault_t SDCard::WriteBlock(const uint32_t Sector,uint8_t *buf)
{
	SDResault_t Res;
	uint32_t Resp;
	uint32_t* pdbuf = (uint32_t*)buf;
	if(((uint32_t)buf%4)!=0)
	{
		std::memcpy((uint8_t*)this->dbuf,buf,512);
		pdbuf = this->dbuf;
	}
	SDIO::CmdInitTypeDef Com;
	SDIO::DataInitTypeDef Dat;
	do
	{
		//CMD13
		Com.CmdIndex = 13;
		Com.Argument = this->RCA << 16;
		Com.CPSM = SDIO_CPSM_Enable;
		Com.Response = SDIO_Response_Short;
		Com.Wait = SDIO_Wait_No;
		Port.SendCommand(Com);
		Res = this->CheckReturn(13, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
		if(Res != OK)
			return Res;
	}
	while((Resp & 0x100) == 0); //READY_FOR DATA
	

	//CMD24
	Com.CmdIndex = 24;
	Com.Argument = Sector;
	Port.SendCommand(Com);
	Res = this->CheckReturn(24, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	

	Dat.DataBlockSize = SDIO_DataBlockSize_512b;
	Dat.DataLength = 512;
	Dat.DataTimeOut = 0xFFFFFFFF;
	Dat.DPSM = SDIO_DPSM_Enable;
	Dat.TransferDir = SDIO_TransferDir_ToCard;
	Dat.TransferMode = SDIO_TransferMode_Block;
	Port.DataConfig(Dat);
	for(int i = 0; i < 512 / 32; i++)
	{
		while(not (_SDIO->STA&SDIO_FLAG_TXFIFOHE));
		for(int j = 0; j < 8; j++)
			_SDIO->FIFO = *pdbuf++;
	}
	while(Port.GetFlagStatus(SDIO_FLAG_TXACT));
	return OK;
}

SDCard::SDResault_t SDCard::ReadBlock(const uint32_t Sector, uint8_t *buf)
{
	uint32_t* pdbuf = (uint32_t*)buf;
	bool cpfl = false;
	if(((uint32_t)buf%4)!=0)
	{
		cpfl = true;
		pdbuf = (uint32_t*)this->dbuf;
	}
	SDResault_t Res;
	uint32_t Resp;
	SDIO::CmdInitTypeDef Com;
	SDIO::DataInitTypeDef Dat;
	
	Dat.DataBlockSize = SDIO_DataBlockSize_512b;
	Dat.DataLength = 512;
	Dat.DataTimeOut = 0xFFFFFFFF;
	Dat.DPSM = SDIO_DPSM_Enable;
	Dat.TransferDir = SDIO_TransferDir_ToSDIO;
	Dat.TransferMode = SDIO_TransferMode_Block;
	Port.DataConfig(Dat);

	do
	{
		//CMD13
		Com.CmdIndex = 13;
		Com.Argument = this->RCA << 16;
		Com.CPSM = SDIO_CPSM_Enable;
		Com.Response = SDIO_Response_Short;
		Com.Wait = SDIO_Wait_No;
		Port.SendCommand(Com);
		Res = this->CheckReturn(13, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
		if(Res != OK)
			return Res;
	}
	while((Resp & 0x100) == 0); //READY_FOR DATA
	
	
	Port.GetFlagStatus(SDIO_FLAG_RXFIFOHF);
	Port.ClearFlag(SDIO_FLAG_RXFIFOHF|SDIO_FLAG_RXOVERR);
	//CMD17
	Com.CmdIndex = 17;
	Com.Argument = Sector;
	Port.SendCommand(Com);
	Res = this->CheckReturn(17, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	for(int i = 0; i < 512 / 32; i++)
	{
		while(not (_SDIO->STA&SDIO_FLAG_RXFIFOHF));
		for(int j = 0; j < 8; j++)
			*pdbuf++ = _SDIO->FIFO;
	}
	while(Port.GetFlagStatus(SDIO_FLAG_RXACT));
	Port.ClearFlag(0xFFFFFF);
	if(cpfl)
		std::memcpy(buf,this->dbuf,512);
	return OK;
}

SDCard::SDResault_t SDCard::SetBolckSize(const uint32_t Size) const
{
	SDResault_t Res;
	uint32_t Resp;
	SDIO::CmdInitTypeDef Com;
	//CMD16
	Com.CmdIndex = 16;
	Com.Argument = Size;
	Com.Response  = SDIO_Response_Short;
	Com.Wait = SDIO_Wait_No;
	Com.CPSM = SDIO_CPSM_Enable;
	Port.SendCommand(Com);
	Res = this->CheckReturn(16, SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND, Resp);
	if(Res != OK)
		return Res;
	return OK;
}

